home *** CD-ROM | disk | FTP | other *** search
- ________________________ Subj: Resetting System Time ________________________
-
- Fm: Mark Betz/GD SL 76605,2346 # 204900
- To: Darrell Fetzer 76636,241 (X) Date: 25-Aug-92 22:47:17
-
- Darrel, this method for calling the original int 8 handler at the proper rate
- for any counter value was passed on to me by a friend in CLMFORUM, Rud
- Merriam. Hope it's useful.
-
- The easiest way to generate approximetly 18.2 pulses is to do the following:
-
- 1. Create a long counter variable.
-
- 2. At every tick of the 8254 add the count loaded into the 8254 to the
- counter.
-
- 3. If the counter exceeds 65k then call the old interrupt vector.
-
- 4. Do a mod 65k on the counter to get the number of ticks over 65k.
-
- That works for any value you put into the 8254 counter.
-
-
- ___________________ Subj: HTimer / High Resolution Timer ___________________
-
- Fm: Mark Betz/GD SL 76605,2346 # 194299
- To: Sarwan Narine 76675,164 Date: 01-Aug-92 14:39:41
-
- Hi, Sarwan.
-
- >> How do you program the timer
-
- You do have a knack for these all-inclusive questions, don't you? <g>. Ok,
- prepare for an exhaustive discussion of the PC timer...On your motherboard
- there is either an 8254 timer chip, or it's equivalent embedded in a VLSI
- chipset. This chip actually consists of 3 seperate timer registers, and a
- status/input register. The three channels are 0, 1, and 2, and they are
- addressed at ports 0x40, 0x41, and 0x42 respectively. Port 0x43 is the
- status/input register, where you send commands or read the timer status. The
- timer channels are actually 16-bit counter registers. Channel 0 is the system
- timer tick, the one we're interested in. Channel 1 is the ram refresh pulse..
- don't mess with it. Channel 2 is hardwired to the PC speaker. It can be used
- to pump digitized sound to the speaker, but we're not getting into that
- today. The way that the counters work is simple: they are loaded with a
- value, and then they count down until the value reaches 0, at which time
- something happens. In the case of channel 1 that something is a system timer
- interrupt (int 08). The _rate_ at which the timers count down is 1.193180
- megahertz. This means that the counter value is decremented 1,193,180 times
- per second. The initial value loaded into the channel 1 register by the bios
- at startup is 0, which is immediately decremented to yield 65535. If you
- divide 1,193,180 / 65535 you get 18.2xxx, which is the approximate default
- rate of the system timer. One other consideration is very important. The
- timer channels operate in several modes. The default mode for channel 1 is
- square wave mode. This means that the timer is decremented by _2_ on each
- pulse, not 1. The first time it hits 0 it is reloaded, and the second time it
- hits 0 the interrupt occurs.
-
- Ok, there are some basic operations you need to do, in this order:
-
- * get the current timer mode so you can save it
- * reprogram the timer to pulse mode
- * load the counter with the right value (roughly 239 for a
- 5000 hz tick)
- * reprogram the timer to it's original values on exit
-
- I was going to attempt to post all the code here, but it's too long. So I'm
- going to email you one file, and suggest downloading two others. None of
- these will show you _exactly_ how to do what you want, but, taken together
- you should be able to figure it out. If you can't you shouldn't be messing
- with the timer anyway <g>. The one I'll mail is TIMER.ZIP, which contains
- some C code I threw together for the last guy who asked this question. The
- other two are TRANS.ZIP and HELPPC.ZIP in LIB 11. TRANS is a hi-res timing
- program I wrote for a compiled bimap competition we had some time ago.
-
- --Mark
- ...........................................................................
-
- Editor's Note : See the following files in GAMERS section 11
-
- HTIMER.ZIP 33K 19-Nov-92 HTimer v1.2, hi-res timing class for BC++
- HTMRMS.ZIP 33K 19-Nov-92 HTimer v1.2, hi-res timing class for MSC++ 7.x
-
- ...........................................................................
-
- Fm: Bob Provencher/GD SL 71621,2632 # 245378
- To: Mark Betz/Ass't SysOp 76605,2346 (X) Date: 15-Nov-92 20:27:49
-
- Hi Mark,
-
- That HTIMER class is pretty wild! You're doin some pretty neat stuff in
- there. I didn't really expect to see anything and I didn't I did get some
- other wierd results. If I do this (I tried the for loop)
-
- while( !kbhit() )
- {
- for ( ; timer.getElapsed() < cycle; )
- ;
- cout << timer.getElapsed() << timer.timerOff() << endl;
- }
-
- The result I get are
-
- 0 100120
-
- And I still get some values below 100000. Is there anything about
- getElapsed() that it can't be called twice in succession like that?
-
- Bob
- ...........................................................................
-
- Fm: Mark Betz/Ass't SysOp 76605,2346 # 245512
- To: Bob Provencher/GD SL 71621,2632 (X) Date: 15-Nov-92 23:53:29
-
- I kind of got a kick out of doing the interface between the class and the
- hardware. That was the neatest part, for me. What I'd eventually like to do,
- after I get the core calculation engine working in all cases, is to extend
- the class so that you have a timer that returns mics, millis, hundredths,
- tenths, and seconds, as well as specialty timers, like one that counts a
- certain amount of time and then makes a callback to your app.
-
- I've been able to show that the problem shows up in getElapsed and timerOff.
- The error is taking place in calcElapsed. What happens is that it
- occasionally returns too large a value. For example, in this code:
-
- timer.timerOn();
- while (time < 100000L) {
- time = timer.getElapsed();
- }
- cout << timer.timerOff() << '\n';
-
- what happens is that you usually exit the while() loop when time > 100000,
- something like 100025. Occasionally getElapsed will return an absurd value,
- like 142000, which bounces you out of the loop. Then timerOff() is called and
- it gets the correct count again, which might be 65000, or whatever. This is
- what was causing the original problem that Jesse saw. This only happens when
- calcElapsed is dealing with a rollover in the counter register; i.e., when
- ticks > 0. At the hardware level, the max mics you can count before a
- register rollover takes place is 65535/1.193, or 54932. Since the register
- can be anywhere in it's count when a timer starts up, this is often much less
- for the first cycle. That's why I hooked int 8 and provided a rolling tick
- counter. Somewhere in the interaction between ticks and register counts I'm
- getting a bogus value.
- ...........................................................................
-
- Fm: Mark Betz/Ass't SysOp 76605,2346 # 245566
- To: Bob Provencher/GD SL 71621,2632 (X) Date: 16-Nov-92 01:58:55
-
- I've got it!! I don't know how to fix it yet, but I found the problem. I owe
- Jesse one for writing code that caused this problem to surface. It's an
- extremely subtle bug that strikes pseudo-randomly (I say pseudo, because it's
- dependent on the resonances between the speed of execution of a polling loop,
- and the speed of the timer chip). Btw, I have to say, with reddened face,
- that I damn well should have caught this myself. It'll show up in any long
- series of short timing cycles. In longer timing cycles it will tend to get
- buried.
-
- This'll be tricky to explain, but I'll give it a shot, since I'd like some
- ideas on how to prevent it. It's really a concurrency problem. The problem
- arises out of the constraints of the timer chip counter registers. Imagine
- the count value in the 16-bit counter along a time line:
-
- count: 65535 32767 0
- |____________________|_____________________|
- | |
- sys event: start cycle int 08
-
- The timer causes an interrupt 8 at "terminal count" ( 0 ), then reloads the
- counter register with 65535 (actually 0 in the default case, which is
- immediately decremented to either 65535 in mode 3, or 65534 in mode 2, before
- counting resumes) and starts over. The whole process is driven at 1.193180
- Mhz, so that you have the counter register decremented 1,193,180 times per
- second. The default frequency for the system timer interrupt, btw, is derived
- from 1,193,180 hz./65535 = 18.2 hz., which is the number of times the process
- reaches terminal count in one second.
-
- You do hi-res timing on the PC by watching the counter register. Each
- decrement in the register is equal to approximately 800 nanoseconds. You
- latch the register count when you start the timer, and latch it again when
- you finish timing. If you're lucky the timeline looks like this:
-
- count: 65535 55432 32767 20013 0
- |_____|______________|_______|_____________|
- | |
- timer: start finish
-
- In this case the calculation is simple: microseconds = (start-finish)/1.193.
- Where you run into trouble is when this happens:
-
- count: 65535 55432 32767 20013 0
- |_____|______________|_______|_____________|
- | |
- timer: finish start
-
- In this case we grabbed the counter when it was at 20013. While we were
- timing it wrapped around, and when we grabbed it again it was at 55432. The
- calculation in this case is microseconds = start + (65535-finish)/1.193. One
- more level of additional complexity exists when the timer wraps more than
- once during a timing session (a "session" is defined as the time between
- calls to either getElapsed or timerOff). In both of these cases you can see
- that the only way for the program to know if the counter has wrapped is to
- watch the timer interrupt. We _know_ that each timer interrupt signals that
- the counter has wrapped.
-
- Now that I've laid the foundation, here's the bug:
-
- count: 65535 43345 32767 2 0
- |___________|________|___________________|_|
- | |
- timer: start finish
-
- The program latches the finishing counter value, 2 in this case. However,
- before it can calculate the elapsed time the counter reaches terminal count
- and issues an interrupt. Now the program thinks there was a wrap in the
- counter, when there actually wasn't one during the timing interval: it
- occured immediately after.
-
- One possible solution is to "lock" the tick counter, so that it cannot be
- modified by the interrupt handler while an elapsed time is being calculated.
-
- The problem with this approach is that you can't sit in the interrupt handler
- waiting for the app to finish so you can update the tick counter. The best
- you can do is set a flag in the interrupt handler so that it will remember to
- update the tick counter on the next pass. This isn't workable, since the next
- opportunity to update the tick counter will be 54,945 microseconds away, and
- that's unacceptable resolution for the timer class.
-
- I apologize for the very long message, but it's a highly technical, and I
- think very interesting problem. If anyone has any ideas I'd love to hear
- them. I need to be able to let the tick counter run free, but somehow
- guarantee that it is valid for a particular calculation of elapsed time.
-
- Ahh, a possible solution just hit me. If the interrupt handler sees that an
- elapsed time is being calculated, it sets a "waiting" flag, instead of
- incrementing the tick counter, and then IRETS. In the HTimer::calcElapsed
- function the code checks the waiting flag after it's done with it's
- calculation, and updates the tick counter itself if the flag is set. What do
- you think?
-
- --Mark
- ...........................................................................
-
- Fm: Mark Betz/Ass't SysOp 76605,2346 # 246084
- To: Bob Provencher/GD SL 71621,2632 (X) Date: 16-Nov-92 22:58:23
-
- Hi. That's a universal truth of bugs <g>. The reason why I didn't catch that
- error was related to the test suite. The tests I created have one fatal flaw:
- they're each performed once! When I saw the correct result I thought,
- "Yippee, it works", and went on about my business. Now I find that there's an
- error that occurs as rarely as once in one thousand cycles. Repetitive
- testing would have caught it. I learned a lesson, and luckily I got to learn
- it on a relatively obscure piece of PD code. If it had been something mission
- critical I would have been burned, or someone else would have, perhaps
- literally. I can take comfort in the fact that, in a mission-critical
- project, someone else would have found it and fired me <g>.
-
- Btw, I tried locking the tick counter, and it improved the situation, but the
- problem didn't go away. I found that I could eliminate it for the end of the
- timing cycle by moving some assignments so that they occured differently with
- relation to the latching of the counter. I also found that the same problem
- was occuring at the start of the timer cycle, in timerOn(). This has proven
- tougher to deal with, but I think I have it subdued for the moment using a
- test for the error condition (an approach I detest, but I'll use it until
- something else works).
-
- The whole thing makes a pretty interesting test case of a real-time
- hardware/software interface problem. I'll upload the current version asap,
- and perhaps we can bang on it enough to make it puke again. I'd like to get
- to the point where HTimer is a 100% solid concurrent multiple-channel timing
- object.
- --Mark
- ...........................................................................
-
-
-